package com.my.core.util.map;

import java.awt.geom.Point2D;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 地图距离
 * @author timo 2021/4/15
 */
public class MapUtils {
//    https://blog.csdn.net/AttleeTao/article/details/105504844

    //private static double EARTH_RADIUS = 6378.137;
    private static double EARTH_RADIUS = 6371.393;
    /**
     * 判断是否在多边形区域内
     * @param pointLon 要判断的点的横坐标 经度
     * @param pointLat 要判断的点的纵坐标 维度
     * @param lon      区域各顶点的横坐标数组
     * @param lat      区域各顶点的纵坐标数组
     * @return
     */
    /*
    对顺序是有要求的,他是根据下标获取经纬度来形成坐标点,然后将所有的坐标点放入集合中形成一个多边形。因为你形成的坐标点错误,所以你最后形成的多边形就会有偏差,判断也会错误
    你可以用这个数据测试一下：
    Double[] arrayx = {83.8437532306271, 83.8488583761132, 83.8518338946186, 83.8518173147173, 83.8511363579565, 83.8510188147665, 83.8509405233864, 83.8508341893464, 83.849093565016, 83.8490750956391, 83.8453724213002, 83.8454498133582, 83.8432496390596, 83.8431323773573, 83.8432464140908, 83.8437272258308};
    Double[] arrayy = {41.31996651, 41.31981426, 41.31967721, 41.31915775, 41.31912048, 41.3176057, 41.31748057, 41.31641269, 41.31643175, 41.31613105, 41.31630375, 41.31775087, 41.31788703, 41.31792243, 41.31915602, 41.31908725};
    System.out.println(regionUtil.isInPolygon(83.848987, 41.318464, arrayx, arrayy));
    System.out.println(regionUtil.isInPolygon(83.844523, 41.317091, arrayx, arrayy));
    */
    public static boolean isInPolygon(double pointLon, double pointLat, Double[] lon,
                                      Double[] lat) {
        // 将要判断的横纵坐标组成一个点
        Point2D.Double point = new Point2D.Double(pointLon, pointLat);
        // 将区域各顶点的横纵坐标放到一个点集合里面
        List<Point2D.Double> pointList = new ArrayList<Point2D.Double>();
        double polygonPoint_x = 0.0, polygonPoint_y = 0.0;
        for (int i = 0; i < lon.length; i++) {
            polygonPoint_x = lon[i];
            polygonPoint_y = lat[i];
            Point2D.Double polygonPoint = new Point2D.Double(polygonPoint_x, polygonPoint_y);
            pointList.add(polygonPoint);
        }
        return check(point, pointList);
    }
    /**
     * 子工具类---不直接使用，使用 isInPolygon
     * @param point   要判断的点的横纵坐标
     * @param polygon 组成的顶点坐标集合
     * @return
     */
    private static boolean check(Point2D.Double point, List<Point2D.Double> polygon) {
        java.awt.geom.GeneralPath peneralPath = new java.awt.geom.GeneralPath();

        Point2D.Double first = polygon.get(0);
        // 通过移动到指定坐标（以双精度指定），将一个点添加到路径中
        peneralPath.moveTo(first.x, first.y);
        polygon.remove(0);
        for (Point2D.Double d : polygon) {
            // 通过绘制一条从当前坐标到新指定坐标（以双精度指定）的直线，将一个点添加到路径中。
            peneralPath.lineTo(d.x, d.y);
        }
        // 将几何多边形封闭
        peneralPath.lineTo(first.x, first.y);
        peneralPath.closePath();
        // 测试指定的 Point2D 是否在 Shape 的边界内。
        return peneralPath.contains(point);
    }
    /**
     * 判断一个点是否在圆形区域内,比较坐标点与圆心的距离是否小于半径
     * @param lat1   纬度
     * @param lat2   纬度
     * @param lng1   经度
     * @param lng2   经度
     * @param radius 要比较的圆心的半径
     */
    public static boolean isInCircle(double lng1, double lat1, double lng2, double lat2, double radius) {
        double distance = getDistance(lat1, lng1, lat2, lng2);
        if (distance > radius) {
            return false;
        } else {
            //System.err.println("经度："+lng1+"维度："+lat1+"经度："+lng2+"维度："+lat2+"距离:"+distance);
            return true;
        }
    }

    /**
     * 角度转弧度
     * @param d
     * @return
     */
    private static double rad(double d)
    {
        return d * Math.PI / 180.0;
    }
    /**
     * 地图坐标距离计算（单位：米）
     * @param lat1
     * @param lng1
     * @param lat2
     * @param lng2
     * @return
     */
    public static double getDistance(double lat1, double lng1, double lat2, double lng2)
    {
        double radLat1 = rad(lat1);
        double radLat2 = rad(lat2);
        double a = radLat1 - radLat2;
        double b = rad(lng1) - rad(lng2);
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2),2) +
                Math.cos(radLat1)*Math.cos(radLat2)*Math.pow(Math.sin(b/2),2)));
        s = s * EARTH_RADIUS;
        s = Math.round(s * 1000);
        return s;
    }


    /*范围签到 返回true false*/
    public static void main(String[] args) {
        //坐标为高德
//        System.out.println(MapUtils.getDistance(29.490295,106.486654,29.615467,106.581515));
        Double[] arrayx = {104.538487, 104.641025, 104.693082, 104.57455};
        Double[] arrayy = {31.039287, 30.92839, 31.158155, 31.166255};
        System.out.println(MapUtils.isInPolygon(104.628405, 31.048748, arrayx, arrayy));
//        System.out.println(MapUtils.isInPolygon(83.844523, 41.317091, arrayx, arrayy));
//[[104.54322,31.064964],[104.615785,30.933802],[104.866607,30.97574],[104.708857,31.155456]]
        Double[] arrayx2 = {104.54322, 104.615785, 104.866607, 104.708857};
        Double[] arrayy2 = {31.064964,30.933802, 30.97574, 31.155456};

        List<Poi> poiList = new ArrayList<Poi>();
        poiList.add(new Poi(104.417226,31.110857));
        poiList.add(new Poi(104.460199,31.109017));
        poiList.add(new Poi(104.460199,31.084915));
        poiList.add(new Poi(104.417226,31.084915));
        BigDecimal area =  getArea(poiList);
        System.out.println( "面积:"+ area + "平方公里" );

        BigDecimal area1 = getArea(arrayx2, arrayy2);
        System.out.println( "面积:"+ area1 + "平方公里" );
    }
    public static BigDecimal getArea(Double[] lon,
                                     Double[] lat){
        List<Poi> ring = new ArrayList<>();
        for(int len = 0;len<lon.length;len ++ ){
            Poi poi = new Poi(lon[len],lat[len]);
            ring.add(poi);
        }
        return getArea(ring);
    }
    public static class Poi{
        private Double lat;
        private Double lng;
        public Poi(Double lng,Double lat){
            this.lat = lat;
            this.lng = lng;
        }
    }
    /**
     * 计算经纬度围成的实际面积
     * @return
     */
    public static BigDecimal getArea(List<Poi> ring){
        double sJ = 6378137;
        double Hq = 0.017453292519943295;
        double c = sJ *Hq;
        double d = 0;

        if (3 > ring.size()) {
            return new BigDecimal( 0);
        }
        for (int i = 0; i < ring.size() - 1; i ++){
            Poi h = ring.get(i);
            Poi k = ring.get(i + 1);
            double u = h.lng * c * Math.cos(h.lat * Hq);

            double hhh = h.lat * c;
            double v = k.lng * c * Math.cos(k.lat *Hq);
            d = d + (u * k.lat * c - v * hhh);
        }
        Poi g1 = ring.get(ring.size()-1);
        Poi point = ring.get(0);
        double eee = g1.lng * c * Math.cos(g1.lat * Hq);
        double g2 = g1.lat * c;

        double k = point.lng * c * Math.cos(point.lat * Hq);
        d += eee * point.lat * c - k * g2;
        return new BigDecimal( 0.5*Math.abs(d)).divide(new BigDecimal(1000000));
    }







    public final static double x_pi = 3.14159265358979324 * 3000.0 / 180.0;

    /**
     * 高德坐标转百度坐标
     *
     * @param gd_lon 经度
     * @param gd_lat 纬度
     * @return
     */
    public static Map<String, Double> gd2bd(double gd_lon, double gd_lat) {
        Map<String, Double> data = new HashMap<>();
        double x = gd_lon, y = gd_lat;
        double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
        double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
        double bd_lon = z * Math.cos(theta) + 0.0065;
        double bd_lat = z * Math.sin(theta) + 0.006;
        data.put("lon", bd_lon);
        data.put("lat", bd_lat);
        return data;
    }


    /**
     * 百度坐标转换高德坐标
     *
     * @param bd_lon 经度
     * @param bd_lat 纬度
     * @return
     */
    public static Map<String, Double> bd2gd(double bd_lon, double bd_lat) {
        double x = bd_lon - 0.0065, y = bd_lat - 0.006;
        double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
        double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
        double gd_lon = z * Math.cos(theta);
        double gd_lat = z * Math.sin(theta);
        Map<String, Double> data = new HashMap<>();
        data.put("lon", gd_lon);
        data.put("lat", gd_lat);
        return data;
    }

}
